package io.kiki.sba.registry.common.model;


import io.kiki.sba.registry.common.model.sessionserver.SimpleSubscriber;
import io.kiki.sba.registry.common.model.store.BaseInfo;
import io.kiki.sba.registry.common.model.store.Subscriber;
import io.kiki.sba.registry.core.model.Scope;
import io.kiki.sba.registry.util.StringFormatter;
import io.kiki.sba.registry.util.StringUtil;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetSocketAddress;
import java.util.*;

public final class SubscriberUtils {
    private static final Logger logger = LoggerFactory.getLogger(SubscriberUtils.class);

    private SubscriberUtils() {
    }

    public static Map<InetSocketAddress, Map<String, Subscriber>> groupBySourceAddress(List<Subscriber> subscriberList) {
        if (subscriberList.isEmpty()) {
            return Collections.emptyMap();
        }
        Map<InetSocketAddress, Map<String, Subscriber>> map = new HashMap<>(128);
        subscriberList.forEach(subscriber -> {
            InetSocketAddress inetSocketAddress = new InetSocketAddress(subscriber.getClientUrl().getIp(), subscriber.getClientUrl().getPort());
            Map<String, Subscriber> registerIdToSubscriberMap = map.computeIfAbsent(inetSocketAddress, k -> new HashMap<>());
            registerIdToSubscriberMap.put(subscriber.getRegisterId(), subscriber);
        });
        return map;
    }

    public static Map<Boolean, List<Subscriber>> groupByMulti(List<Subscriber> subscriberList) {

        if (subscriberList.isEmpty()) {
            return Collections.emptyMap();
        }
        Map<Boolean, List<Subscriber>> map = new HashMap<>(2);
        for (Subscriber subscriber : subscriberList) {
            List<Subscriber> list = map.computeIfAbsent(subscriber.isAcceptMulti(), k -> new ArrayList<>());
            list.add(subscriber);
        }

        return map;
    }

    public static Map<Scope, List<Subscriber>> groupByScope(List<Subscriber> subscriberList) {
        if (subscriberList.isEmpty()) {
            return Collections.emptyMap();
        }
        Map<Scope, List<Subscriber>> scopeToSubscriberListMap = new HashMap<>();
        for (Subscriber subscriber : subscriberList) {
            final Scope scope = subscriber.getScope();
            if (scope == null) {
                logger.warn("Nil ScopeEnum, {}", subscriber);
                continue;
            }
            List<Subscriber> subscriberList1 = scopeToSubscriberListMap.computeIfAbsent(scope, scope1 -> new ArrayList<>());
            subscriberList1.add(subscriber);
        }
        return scopeToSubscriberListMap;
    }

    public static Scope getAndAssertHasSameScope(Collection<Subscriber> subscribers) {
        Iterator<Subscriber> iterator = subscribers.iterator();
        Subscriber first = iterator.next();
        Scope scope = first.getScope();
        while (iterator.hasNext()) {
            Subscriber subscriber = iterator.next();
            if (scope != subscriber.getScope()) {
                throw new RuntimeException(StringFormatter.format("conflict scope, one is {}, anther is {}", first.shortDesc(), subscriber.shortDesc()));
            }
        }
        return scope;
    }

    public static String[] getAndAssertAcceptedEncodes(Collection<Subscriber> subscribers) {
        Iterator<Subscriber> iterator = subscribers.iterator();
        Subscriber first = iterator.next();
        String[] acceptEncodes = first.getAcceptEncodes();
        while (iterator.hasNext()) {
            Subscriber subscriber = iterator.next();
            if (!Arrays.equals(acceptEncodes, subscriber.getAcceptEncodes())) {
                throw new RuntimeException(StringFormatter.format("conflict encoding, one is {}, anther is {}", first.shortDesc(), subscriber.shortDesc()));
            }
        }
        return acceptEncodes;
    }

    public static boolean getAndAssertAcceptMulti(Collection<Subscriber> subscribers) {
        Iterator<Subscriber> iterator = subscribers.iterator();
        Subscriber first = iterator.next();
        boolean acceptMulti = first.isAcceptMulti();
        while (iterator.hasNext()) {
            Subscriber subscriber = iterator.next();
            if (acceptMulti != subscriber.isAcceptMulti()) {
                throw new RuntimeException(StringFormatter.format("conflict acceptMulti, one is {}, anther is {}", first.shortDesc(), subscriber.shortDesc()));
            }
        }
        return acceptMulti;
    }

    public static void assertClientVersion(Collection<Subscriber> subscribers, BaseInfo.ClientVersion clientVersion) {
        for (Subscriber sub : subscribers) {
            StringUtil.checkEquals(sub.getClientVersion(), clientVersion, "subscriber.clientVersion");
        }
    }

    public static long getMinRegisterTimestamp(Collection<Subscriber> subscribers) {
        long min = Long.MAX_VALUE;
        for (Subscriber sub : subscribers) {
            long v = sub.getRegisterTimestamp();
            if (min > v) {
                min = v;
            }
        }
        return min;
    }

    public static SimpleSubscriber convert(Subscriber subscriber) {
        return new SimpleSubscriber(subscriber.getClientId(), subscriber.getClientUrl().buildAddressString(), subscriber.getAppName());
    }

    public static List<SimpleSubscriber> convert(Collection<Subscriber> subscribers) {
        if (CollectionUtils.isEmpty(subscribers)) {
            return Collections.emptyList();
        }
        List<SimpleSubscriber> ret = new ArrayList<>(subscribers.size());
        for (Subscriber s : subscribers) {
            ret.add(convert(s));
        }
        return ret;
    }
}
